package handler

import (
	"github.com/gin-gonic/gin"
	"internal/api/auth"
	"internal/util"
	"net/http"
	"strconv"
	"time"
)

// *************************************************** 自定义中间件 ***************************************************

/*
	%3d：
	% 表示开始一个占位符。
	3 表示宽度，即至少占用3个字符的宽度。如果数值的字符数少于3，则默认右对齐，左侧填充空格。
	d 表示要格式化的数据类型是十进制整数（int）。
	%8v：
	% 表示开始一个占位符。
	8 表示宽度，即至少占用8个字符的宽度。如果值的字符数少于8，则默认右对齐，左侧填充空格。
	v 是一个通用的值占位符，可以用于任何类型的值。在 fmt 包中，%v 会根据值的类型以默认格式打印该值。
	%15s：
	% 表示开始一个占位符。
	15 表示宽度，即至少占用15个字符的宽度。如果字符串的字符数少于15，则默认右对齐，左侧填充空格。
	s 表示要格式化的数据类型是字符串（string）
*/

// GinLogger 日志中间件
func GinLogger() gin.HandlerFunc {
	/*
		区别:
		time.Since(start) 直接从start到当前时间计算持续时间，而不需要显式提供当前时间。
		end.Sub(start) 需要两个时间点start和end，并且明确计算这两个时间点之间的持续时间。
	*/
	return func(c *gin.Context) {
		// 开始时间
		start := time.Now()
		// 处理请求
		c.Next()
		end := time.Now()
		latency := end.Sub(start)

		// 自定义输出格式
		util.LogSrv.Info("[RESPONSE] %3d | %8v | %15s | %s %s |",
			c.Writer.Status(),
			latency,
			c.ClientIP(),
			c.Request.Method,
			c.Request.RequestURI,
		)
	}
}

// CorsMiddle 跨域中间件
func CorsMiddle() gin.HandlerFunc {
	/*
		c.Header("Access-Control-Allow-Origin", "*")：
		设置响应头Access-Control-Allow-Origin为*，表示允许所有域进行跨源请求。出于安全考虑，通常建议指定具体的域而不是使用*。

		c.Header("Access-Control-Allow-Credentials", "true")：
		设置响应头Access-Control-Allow-Credentials为true，表示允许携带凭证信息（如Cookies）的跨源请求。当Access-Control-Allow-Origin设置为*时，这个值必须设置为false，否则浏览器会阻止请求。

		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,X-Token,X-User-Id")：
		设置响应头Access-Control-Allow-Headers，指定允许的HTTP请求头。这通常用于预检请求（preflight request），告诉浏览器服务器支持哪些自定义的HTTP请求头。

		注释掉的c.Header("Access-Control-Allow-Origin", c.Request.Header.Get("Origin"))：
		这行代码被注释掉了，如果取消注释，它将动态地将Access-Control-Allow-Origin设置为请求头中的Origin值，这样可以只允许请求来源的域进行跨源请求，而不是所有域。

		注释掉的c.Header("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PUT")：
		这行代码被注释掉了，如果取消注释，它将设置响应头Access-Control-Allow-Methods，指定允许的HTTP方法。

		注释掉的c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")：
		这行代码被注释掉了，如果取消注释，它将设置响应头Access-Control-Expose-Headers，指定哪些响应头可以被浏览器的JavaScript访问。
	*/
	return func(c *gin.Context) {
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Credentials", "true")
		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,X-Token,X-User-Id")

		// 放行所有操作方法
		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		// 处理请求
		c.Next()
	}
}

// AuthorizeMiddle 鉴权中间键
func AuthorizeMiddle() gin.HandlerFunc {
	return func(c *gin.Context) {
		if !WithinWhiteList(c.Request.URL, c.Request.Method) {
			strToken := c.DefaultQuery("accesstoken", "")
			if strToken == "" {
				c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
					"code": 40001,
					"msg":  "access_token error",
				})
				return
			}
			token, err := auth.ParesToken(strToken)
			if err != nil {
				util.LogSrv.Warn("[AuthorizeMiddle] strToken=[%s] , err = %v", strToken, err)
				c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
					"code": 40001,
					"msg":  "access_token error",
				})
				return
			}
			c.Set("devicon", strconv.FormatInt(token.UserId, 10))
			util.LogSrv.Debug("[AuthorizeMiddle] tokenInfo=%v", token)
		}
		c.Next()
	}
}
